#include "TextReader.h"

int32 StreamReader::Initalize()
{
	m_encoding     = 0;
	m_stream       = 0;
	m_userStream   = false;
	m_userEncoding = false;

	m_bytes = 0;
	m_chars = 0;

	m_byteSize = 0;
	m_charSize = 0;
	m_bytePos  = 0;
	m_charPos  = 0;

	m_readSize = 0;
	m_saveSize = 0;

	return R_SUCCEED;
}

int32 StreamReader::Initalize( Stream* stream , Encoding* encoding , bool autoReadBom )
{
	if( stream   == 0 )
		return E_STREAMREADER_INVALID_STREAM;

	if( stream->CanRead() != R_SUCCEED )
		return E_STREAMREADER_INVALID_STREAM;


	if( stream->HaveEOS() != R_SUCCEED )
		return E_STREAMREADER_INVALID_EOS;

	m_stream = stream;

	if( encoding == 0 )
	{
		autoReadBom = true;
		m_encoding = Encoding::Create(ENCODING_UTF8);
		if(m_encoding == 0)
		{
			Uninitalize();
			return E_STREAMREADER_CREATE_ENCODING;
		}
	}
	else
	{
		m_encoding     = encoding;
		m_userEncoding = true;
	}

	int32  code      = R_SUCCEED;
	uint32 charSize = 0;

	code = m_encoding->GetCharSize(charSize);

	if(code != R_SUCCEED)
	{
		Uninitalize();
		return code;
	}

	uint32 uCharSize = 0;
	code = m_encoding->GetCharSize( uCharSize );

	if( code != R_SUCCEED )
		return code;

	uint32 allocBytes = MAX_READ_SIZE;
	uint32 allocChars = MAX_READ_SIZE * uCharSize;

	m_chars = (tchar*)malloc( allocChars * sizeof(tchar) + sizeof(tchar) );
	m_bytes = (byte*)malloc( allocBytes );

	if( m_chars == 0 || m_bytes == 0 )
	{
		Uninitalize();
		return E_STREAMREADER_ALLOCATE;
	}

	m_readSize = allocBytes;
	m_saveSize = allocChars;

	if( code != R_SUCCEED )
	{
		Uninitalize();
		return code;
	}

	code = DecodeStream(autoReadBom);

	if( code != R_SUCCEED )
		Uninitalize();
	return code;
}

int32 StreamReader::Initalize( cstring& path , Encoding* encoding )
{
	int32 code = R_SUCCEED;

	if( m_stream != 0 )
		Uninitalize();

	FileStream* stream = new FileStream();
	m_userStream = true;

	code = stream->Open( path.c_str() );

	if(code != R_SUCCEED)
	{
		Uninitalize();
		return code;
	}

	return Initalize( stream , encoding );
}

int32 StreamReader::Initalize( cstring& path )
{
	return Initalize( path , 0 );
}

int32 StreamReader::Initalize( Stream* stream )
{
	if(m_stream != 0)
		Uninitalize();

	return Initalize(stream,0,true);
}

int32 StreamReader::Initalize( Stream* stream , Encoding* encoding )
{
	if(m_stream != 0)
		Uninitalize();

	return Initalize( stream , encoding , false );
}

int32 StreamReader::ReadChars( uint32 offset /*= 0 */ )
{
	int32 code = R_SUCCEED;

	uint32 useCharSize = 0;
	uint32 useByteSize = 0;
	code = m_encoding->Decode( m_bytes + offset , m_byteSize - offset , m_chars , m_saveSize , useCharSize , useByteSize );

	if( code != R_SUCCEED )
		return code;

	m_charPos  = 0;
	m_charSize = useCharSize;
	m_bytePos  = useByteSize + offset;
	m_chars[m_charSize] = 0;
	return code;
}

int32 StreamReader::ReadStream()
{
	int32 code = R_SUCCEED;

	uint32 offset    = 0;
	uint32 readSize  = m_readSize;
	uint32 readBytes = 0;
	uint32 copySize  = 0;

	if( m_bytePos != m_byteSize )
	{
		copySize = m_byteSize - m_bytePos;
		readSize = m_readSize - copySize;
		offset   = m_byteSize - copySize;
	}

	if( copySize > 0 )
		memcpy( m_bytes , m_bytes + m_bytePos , copySize );

	code = m_stream->Read( m_bytes + copySize , readSize , readBytes );

	m_byteSize = readBytes + copySize;
	m_bytePos  = 0;

	return code;
}

bool StreamReader::CheckByte( const int32& byte1 , const int32& byte2 )
{
	return ( m_bytes[0] == byte1 && m_bytes[1] == byte2 );
}

bool StreamReader::CheckByte( const int32& byte1 , const int32& byte2 , const int32& byte3 )
{
	return ( m_bytes[0] == byte1 && m_bytes[1] == byte2 && m_bytes[2] == byte3 );
}

bool StreamReader::CheckByteEx( const int32& byte1 , const int32& byte2 )
{
	return ( m_bytes[2] == byte1 && m_bytes[3] == byte2 );
}

int32 StreamReader::DecodeStream( bool autoReadBom )
{
	int32 code = ReadStream();

	if( code != R_SUCCEED )
		return code;

	EncodingType bomEncoding = ENCODING_UNKNOW;
	bool         allocBytes  = false;
	uint32 byteSize = 0;

	uint32 uOldCharSize = 0;
	uint32 uNewCharSize = 0;

	if( autoReadBom )
	{

		if( m_byteSize < 2 )
			goto InitChars;

		if( CheckByte(0xFE,0xFF) )
		{
			byteSize    = 2;
			bomEncoding = ENCODING_UNICODE_BIGEND;
		}
		else if( CheckByte(0xFF,0xFE) )
		{
			if( m_byteSize >=4 && CheckByteEx(0x0,0x0) )
			{
				byteSize    = 4;
				bomEncoding = ENCODING_UTF32;
			}
			else
			{
				byteSize    = 2;
				bomEncoding = ENCODING_UNICODE;
			}
		}
		else if( m_byteSize >= 3 && CheckByte( 0xEF , 0xBB , 0xBF ) )
		{
			byteSize    = 3;
			bomEncoding = ENCODING_UTF8;
		}
		else if( m_byteSize >= 4 && CheckByte( 0x0 , 0x0 ) && CheckByteEx(0xFE,0xFF ) )
		{
			byteSize    = 4;
			bomEncoding = ENCODING_UTF32_BIGEND;

		}

		if( bomEncoding != ENCODING_UNKNOW )
		{

			code = m_encoding->GetCharSize(uOldCharSize);

			if( code != R_SUCCEED )
				return code;

			if( !m_userEncoding  )
			{
				Encoding::Destroy( m_encoding );
			}

			m_encoding = 0;
			m_encoding = Encoding::Create( bomEncoding );
			if( m_encoding == 0 )
				return E_STREAMREADER_CREATE_ENCODING;

			code = m_encoding->GetCharSize(uNewCharSize);

			if( code != R_SUCCEED )
				return code;

			m_userEncoding = true;
		}

	}
InitChars:

	if( uOldCharSize != uNewCharSize )
	{
		free( m_chars );
		m_chars = (tchar*)malloc( uNewCharSize );
		if(m_chars == 0)
			return E_STREAMREADER_ALLOCATE;
	}

	if( byteSize == 0 )
	{
		uint32 perambleSize = 0;
		code = m_encoding->GetPerambleSize(perambleSize);
		if( m_byteSize >= perambleSize )
		{
			if( perambleSize < 10 )
			{
				byte peramble[10];
				code = m_encoding->GetPeramble(peramble,sizeof(perambleSize));
				for(uint32 ix=0;ix<perambleSize;++ix)
				{
					if( m_bytes[ix] == peramble[ix] )
						byteSize++;
				}
			}
			else
			{
				byte* permable = (byte*)malloc(perambleSize);
				if(permable == 0)
					return E_STREAMREADER_PERAMBLE_ALLOC;
				for(uint32 ix=0;ix<perambleSize;++ix)
				{
					if(m_bytes[ix] == permable[ix])
						byteSize++;
				}
			}

			if(byteSize != perambleSize)
				byteSize = 0;
		}
	}

	code = ReadChars( byteSize );
	return code;
}

int32 StreamReader::ReadBuffer()
{
	int32 code = ReadStream();

	if(code != R_SUCCEED)
		return code;

	code = ReadChars();

	return code;
}

int32 StreamReader::Uninitalize()
{
	if( !m_userStream )
	{
		if( m_stream != 0 )
			delete  m_stream;
	}

	if( !m_userEncoding )
	{
		if( m_encoding != 0 )
			Encoding::Destroy(m_encoding);
	}

	if( m_chars != 0 )
	{
		free(m_chars);
	}

	if( m_bytes != 0 )
	{
		free(m_bytes);
	}

	Initalize();
	return R_SUCCEED;
}

StreamReader::StreamReader()
{
	Initalize();
}

bool StreamReader::EndStream()
{
	if( m_stream == 0 )
		return true;

	if( m_stream->EndOfStream() == R_SUCCEED )
		return true;

	return false;
}

int32 StreamReader::EndOfStream()
{
	if( m_charPos >= m_charSize && EndStream()  )
		return R_SUCCEED;
	return R_STREAMREADER_NOT_EOS;
}

int32 StreamReader::Read( tchar& value )
{
	int32 code = R_SUCCEED;
	if( m_charPos >= m_charSize )
	{
		if( EndStream() )
			return E_STREAMREADER_EOS;
		else
			code = ReadBuffer();
	}

	if(code != R_SUCCEED)
		return code;

	if( m_charPos == m_charSize )
		return E_STREAMREADER_EOS;
	else
		value = m_chars[m_charPos++];
	return R_SUCCEED;
}

int32 StreamReader::ReadToEnd( string& value )
{
	value.clear();
	int32 code = R_SUCCEED;

	if( m_charPos < m_charSize )
	{
		value.append( m_chars + m_charPos , m_charSize - m_charPos );
	}

	while( !EndStream() )
	{
		code = ReadBuffer();
		if( code != R_SUCCEED )
			return code;
		value.append( m_chars,m_charSize);
	}

	m_charPos = m_charSize;

	return R_SUCCEED;
}

int32 StreamReader::ReadLine( string& value )
{
	value.clear();
	int32 code = R_SUCCEED;
	if( EndOfStream() == R_SUCCEED )
		return E_STREAMREADER_EOS;
	

	uint32 posStart = m_charPos;

	while(true)
	{
		if( m_charPos == m_charSize )
		{
			value.append( m_chars + m_charPos , m_charSize - posStart );

			if(EndStream())
				break;

			code = ReadBuffer();
			if(code != R_SUCCEED)
				return code;
			posStart = m_charPos;
		}

		if( m_chars[m_charPos] == _RBT('\n') )
		{
			value.append(m_chars + m_charPos , m_charPos - posStart);
			if( *(value.end()) == _RBT('\r') )
				value.erase(value.end());
			break;
		}

		m_charPos++;
	}

	return R_SUCCEED;
}
